package com.herohead;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Collection;

import cryptix.message.EncryptedMessage;
import cryptix.message.EncryptedMessageBuilder;
import cryptix.message.KeyBundleMessage;
import cryptix.message.LiteralMessage;
import cryptix.message.LiteralMessageBuilder;
import cryptix.message.Message;
import cryptix.message.MessageException;
import cryptix.message.MessageFactory;
import cryptix.message.NotEncryptedToParameterException;
import cryptix.message.SignedMessage;
import cryptix.openpgp.PGPArmouredMessage;
import cryptix.pki.KeyBundle;
import cryptix.pki.KeyID;
import cryptix.pki.KeyIDFactory;

/** Webmethods version of PGP
 * there is nothing special in this code to do with
 * webmethods. The other versions actually exited the code
 * if the keys was not found
 * 
 */

public class PGPwmv2 {

	static final String toHexString(int aNumber)
	{
		StringBuffer            theBuffer = new StringBuffer(8);
		int                     shift = 28;
		for (int i = 0; i< 8; i++)
		{
			int             aNibble = (aNumber >> shift) & 0x0000000F;
			char    aNib;
			if(aNibble <= 0x9)
			{
				aNib = (char) (aNibble + 48);
			}
			else
			{
				aNib = (char) (aNibble + 87);
			}
			theBuffer.append(aNib);
			shift -= 4;

		}
		return(theBuffer.toString());

	}
	boolean armourFile;
	boolean compress;
	String pubKeyID;

	String privKeyID;
	private byte[] armourMessage (Message msg) {

		byte[] b = null;

		try {
			PGPArmouredMessage armoured;
			armoured = new PGPArmouredMessage(msg);
			b = armoured.getEncoded();

		} catch (MessageException me) {
			System.err.println("Writing the encrypted message failed.");
			me.printStackTrace();
		}

		return b;
	}
	/**
	 * check if the key file exists
	 * @param fileName
	 * @return - true or false
	 */
	public boolean checkForKeyFile (String fileName) {

		File keyFile = new File(fileName);
		if (keyFile.canRead()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * decrypt bytes
	 * @param msgBytes
	 * @param privKey
	 * @return returns error
	 * @throws Error
	 */

	@SuppressWarnings({ "unused", "rawtypes" })
	public byte[] decrypt (byte[] msgBytes, String privKey) throws Error {



		//**********************************************************************
		// Dynamically register both the Cryptix JCE and Cryptix OpenPGP
		// providers.
		//**********************************************************************
		java.security.Security.addProvider(
				new cryptix.jce.provider.CryptixCrypto() );
		java.security.Security.addProvider(
				new cryptix.openpgp.provider.CryptixOpenPGP() );


		//**********************************************************************
		// First read the keys.
		//**********************************************************************
		KeyBundle secretBundle   = null;
		KeyBundle publicBundle = null;
		MessageFactory mf     = null;
		byte[] decryptedBytes = null;


		try {

			FileInputStream in; Collection msgs; KeyBundleMessage kbm;

			mf = MessageFactory.getInstance("OpenPGP");

			in = new FileInputStream(privKey);
			msgs = mf.generateMessages(in);
			kbm = (KeyBundleMessage)msgs.iterator().next();
			secretBundle = kbm.getKeyBundle();
			in.close();
			setPrivKeyID(getKeyID(secretBundle));

		} catch (IOException ioe) {
			System.err.println("IOException... You did remember to run the "+
					"GenerateAndWriteKey example first, right?");
			ioe.printStackTrace();
			throw new Error(ioe);
		} catch (NoSuchAlgorithmException nsae) {
			System.err.println("Cannot find the OpenPGP MessageFactory. "+
					"This usually means that the Cryptix OpenPGP provider is not "+
					"installed correctly.");
			nsae.printStackTrace();
			throw new Error(nsae);
		} catch (MessageException me) {
			System.err.println("Reading keybundle failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		//  Read the message.
		//**********************************************************************
		Message msg = null;

		try {

			//FileInputStream in = new FileInputStream("e:/dnb/testfile.mtc.pgp");
			DataInputStream in = new DataInputStream(new ByteArrayInputStream(msgBytes));
			Collection msgs = mf.generateMessages(in);
			msg = (Message)msgs.iterator().next();

			in.close();

		} catch (IOException ioe) {
			System.err.println("IOException... You did remember to run the "+
					"Encrypt example first, right?");
			ioe.printStackTrace();
			throw new Error(ioe);
		} catch (MessageException me) {
			System.err.println("Reading message failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		// Decrypt the message (if encrypted).
		//**********************************************************************
		try {

			if (msg instanceof EncryptedMessage) {

				msg = ((EncryptedMessage)msg).decrypt(secretBundle,
						"TestingPassphrase".toCharArray());

				System.out.println("Message has been decrypted.");

			} else {

				System.out.println("Message is not encrypted.");

			}

		} catch (NotEncryptedToParameterException netpe) {
			System.err.println("Not encrypted to this key.");
			netpe.printStackTrace();
			throw new Error(netpe);
		} catch (UnrecoverableKeyException uke) {
			System.err.println("Invalid passphrase.");
			uke.printStackTrace();
			throw new Error(uke);
		} catch (MessageException me) {
			System.err.println("Decrypting message failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		// Show the original message
		//**********************************************************************

		try {

			decryptedBytes = (((LiteralMessage)msg).getBinaryData());

		} catch (MessageException me) {
			System.err.println("Retrieving the original message failed.");
			me.printStackTrace();
			throw new Error(me);
		}

		return decryptedBytes;
	}

	@SuppressWarnings("rawtypes")
	public byte[] decryptAndVerify (byte[] msgBytes, String privKey, String pubKey) throws Error {



		//**********************************************************************
		// Dynamically register both the Cryptix JCE and Cryptix OpenPGP
		// providers.
		//**********************************************************************
		java.security.Security.addProvider(
				new cryptix.jce.provider.CryptixCrypto() );
		java.security.Security.addProvider(
				new cryptix.openpgp.provider.CryptixOpenPGP() );


		//**********************************************************************
		// First read the keys.
		//**********************************************************************
		KeyBundle secretBundle   = null;
		KeyBundle publicBundle = null;
		MessageFactory mf     = null;
		byte[] decryptedBytes = null;


		try {

			FileInputStream in; Collection msgs; KeyBundleMessage kbm;

			mf = MessageFactory.getInstance("OpenPGP");

			in = new FileInputStream(privKey);
			msgs = mf.generateMessages(in);
			kbm = (KeyBundleMessage)msgs.iterator().next();
			secretBundle = kbm.getKeyBundle();
			in.close();
			setPrivKeyID(getKeyID(secretBundle));


			in = new FileInputStream(pubKey);
			msgs = mf.generateMessages(in);
			kbm = (KeyBundleMessage)msgs.iterator().next();
			publicBundle = kbm.getKeyBundle();
			in.close();
			setPubKeyID(getKeyID(publicBundle));

		} catch (IOException ioe) {
			System.err.println("IOException... You did remember to run the "+
					"GenerateAndWriteKey example first, right?");
			ioe.printStackTrace();
			throw new Error(ioe);
		} catch (NoSuchAlgorithmException nsae) {
			System.err.println("Cannot find the OpenPGP MessageFactory. "+
					"This usually means that the Cryptix OpenPGP provider is not "+
					"installed correctly.");
			nsae.printStackTrace();
			throw new Error(nsae);
		} catch (MessageException me) {
			System.err.println("Reading keybundle failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		//  Read the message.
		//**********************************************************************
		Message msg = null;

		try {

			//FileInputStream in = new FileInputStream("e:/dnb/testfile.mtc.pgp");
			DataInputStream in = new DataInputStream(new ByteArrayInputStream(msgBytes));
			Collection msgs = mf.generateMessages(in);
			msg = (Message)msgs.iterator().next();

			in.close();

		} catch (IOException ioe) {
			System.err.println("IOException... You did remember to run the "+
					"Encrypt example first, right?");
			ioe.printStackTrace();
			throw new Error(ioe);
		} catch (MessageException me) {
			System.err.println("Reading message failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		// Decrypt the message (if encrypted).
		//**********************************************************************
		try {

			if (msg instanceof EncryptedMessage) {

				msg = ((EncryptedMessage)msg).decrypt(secretBundle,
						"TestingPassphrase".toCharArray());

				System.out.println("Message has been decrypted.");

			} else {

				System.out.println("Message is not encrypted.");

			}

		} catch (NotEncryptedToParameterException netpe) {
			System.err.println("Not encrypted to this key.");
			netpe.printStackTrace();
			throw new Error(netpe);
		} catch (UnrecoverableKeyException uke) {
			System.err.println("Invalid passphrase.");
			uke.printStackTrace();
			throw new Error(uke);
		} catch (MessageException me) {
			System.err.println("Decrypting message failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		// Verify the signature (if signed).
		//**********************************************************************
		try {

			if (msg instanceof SignedMessage) {

				if (((SignedMessage)msg).verify(publicBundle)) {
					System.out.println("Signature OK.");
				} else {
					System.out.println("Signature BAD!");
				}

				msg = ((SignedMessage)msg).getContents();

			} else {

				System.out.println("Message is not signed.");

			}

		} catch (MessageException me) {
			System.err.println("Verifying the signature failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		// Show the original message
		//**********************************************************************

		try {

			decryptedBytes = (((LiteralMessage)msg).getBinaryData());

		} catch (MessageException me) {
			System.err.println("Retrieving the original message failed.");
			me.printStackTrace();
			throw new Error(me);
		}

		return decryptedBytes;
	}

	/**
	 * encrypt -
	 * @param msgBytes
	 * @param pubKey
	 * @return
	 */

	@SuppressWarnings("rawtypes")
	public byte[] encrypt (byte[] msgBytes , String pubKey) {

		//**********************************************************************
		// Dynamically register both the Cryptix JCE and Cryptix OpenPGP
		// providers.
		//**********************************************************************
		java.security.Security.addProvider(
				new cryptix.jce.provider.CryptixCrypto() );
		java.security.Security.addProvider(
				new cryptix.openpgp.provider.CryptixOpenPGP() );


		//**********************************************************************
		// First read the key.
		//**********************************************************************
		KeyBundle publicBundle = null;
		byte[] encryptedBytes = null;

		try {

			FileInputStream in = new FileInputStream(pubKey);

			MessageFactory mf = MessageFactory.getInstance("OpenPGP");
			Collection msgs = mf.generateMessages(in);
			KeyBundleMessage kbm = (KeyBundleMessage)msgs.iterator().next();
			publicBundle = kbm.getKeyBundle();
			in.close();
			setPubKeyID(getKeyID(publicBundle));

		} catch (IOException ioe) {
			System.err.println("IOException... You did remember to run the "+
					"GenerateAndWriteKey example first, right?");
			ioe.printStackTrace();
			throw new Error(ioe);
		} catch (NoSuchAlgorithmException nsae) {
			System.err.println("Cannot find the OpenPGP MessageFactory. "+
					"This usually means that the Cryptix OpenPGP provider is not "+
					"installed correctly.");
			nsae.printStackTrace();
			throw new Error(nsae);
		} catch (MessageException me) {
			System.err.println("Reading keybundle failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		//  Build the literal message.
		//**********************************************************************
		LiteralMessage litmsg = null;
		try {

			LiteralMessageBuilder lmb =
					LiteralMessageBuilder.getInstance("OpenPGP");
			lmb.init(msgBytes);
			litmsg = (LiteralMessage)lmb.build();
		} catch (NoSuchAlgorithmException nsae) {
			System.err.println("Cannot find the OpenPGP LiteralMessageBuilder."+
					" This usually means that the Cryptix OpenPGP provider is not "+
					"installed correctly.");
			nsae.printStackTrace();
			throw new Error(nsae);
		} catch (MessageException me) {
			System.err.println("Creating the literal message failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		// Encrypt the message.
		//**********************************************************************
		Message msg = null;
		try {

			EncryptedMessageBuilder emb =
					EncryptedMessageBuilder.getInstance("OpenPGP");
			emb.init(litmsg);
			emb.addRecipient(publicBundle);
			if (getCompress()) {
				emb.setAttribute("compressed", "true");
			}
			msg = emb.build();

		} catch (NoSuchAlgorithmException nsae) {
			System.err.println("Cannot find the OpenPGP "+
					"EncryptedMessageBuilder. "+
					"This usually means that the Cryptix OpenPGP provider is not "+
					"installed correctly.");
			nsae.printStackTrace();
			throw new Error(nsae);
		} catch (MessageException me) {
			System.err.println("Creating the encrypted message failed.");
			me.printStackTrace();
			throw new Error(me);
		}


		//**********************************************************************
		// Armour the message and write it to disk
		//**********************************************************************
		try {

			if (armourFile) {
				encryptedBytes = armourMessage(msg);
			} else {
				encryptedBytes = msg.getEncoded();
			}
		} catch (MessageException me) {
			throw new Error(me + ":Reading message failed");
		}

		return encryptedBytes;

	}
	@SuppressWarnings("unused")
	private boolean getArmour () {
		return armourFile;
	}
	/**
	 * getBytesFromFile - gets the bytes from a file
	 * @param file
	 * @return - bytes
	 * @throws IOException
	 */

	@SuppressWarnings("resource")
	public byte[] getBytesFromFile(File file) throws IOException {
		InputStream is = new FileInputStream(file);

		// Get the size of the file
		long length = file.length();

		// You cannot create an array using a long type.
		// It needs to be an int type.
		// Before converting to an int type, check
		// to ensure that file is not larger than Integer.MAX_VALUE.
		if (length > Integer.MAX_VALUE) {
			// File is too large
		}

		// Create the byte array to hold the data
		byte[] bytes = new byte[(int)length];

		// Read in the bytes
		int offset = 0;
		int numRead = 0;
		while (offset < bytes.length
				&& (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
			offset += numRead;
		}

		// Ensure all the bytes have been read in
		if (offset < bytes.length) {
			throw new IOException("Could not completely read file "+file.getName());
		}

		// Close the input stream and return bytes
		is.close();
		return bytes;
	}
	private boolean getCompress () {
		return compress;
	}
	@SuppressWarnings("unused")
	private String getKeyID (KeyBundle keybundle) {

		try {

			Key key = (Key)keybundle.getPublicKeys().next();
			KeyIDFactory kidf = KeyIDFactory.getInstance("OpenPGP");
			KeyID id = kidf.generateKeyID(key);

			byte[] fingerprint = id.getBytes();
			byte[] keyid = id.getBytes(4);

			DataInputStream datainput =
					new DataInputStream(
							new ByteArrayInputStream(keyid));

			int kid = datainput.readInt();
			datainput.close();

			return toHexString(kid);
		}
		catch (IOException ioe) {
			System.err.println("IOException... You did remember to run the "+
					"GenerateAndWriteKey example first, right?");
			ioe.printStackTrace();
			throw new Error(ioe);
		} catch (NoSuchAlgorithmException nsae) {
			System.err.println("Cannot find the OpenPGP MessageFactory. "+
					"This usually means that the Cryptix OpenPGP provider is not "+
					"installed correctly.");
			nsae.printStackTrace();
			throw new Error(nsae);
		}  catch (InvalidKeyException me) {
			System.err.println("Invalid Key.");
			me.printStackTrace();
			throw new Error(me);
		}

	}

	public String getPrivKeyID () {
		return privKeyID;
	}
	public String getPubKeyID () {
		return pubKeyID;
	}
	/**
	 * setAmour - armour the encrypt message
	 * @param armourValue - true or flase
	 */
	public void setArmour(boolean armourValue) {
		armourFile = armourValue;
	}
	/**
	 * setCompress - compress the encrypted message
	 * @param compressValue - true or false
	 */
	public void setCompress(boolean compressValue) {
		compress = compressValue;
	}
	private void setPrivKeyID (String keyID){
		privKeyID = keyID;
	}

	private void setPubKeyID (String keyID){
		pubKeyID = keyID;
	}

}
